package gbench.appdemo.ggplot.layer.geom;

import java.util.stream.Collectors;
import java.util.stream.Stream;

import gbench.common.matlib.MatlibCanvas.NVec;
import gbench.common.matlib.MatlibCanvas.Range;
import gbench.common.matlib.data.DataReader.DFrame;
import gbench.common.matlib.data.Pipe;
import gbench.common.matlib.img.ImageOps;
import gbench.common.matlib.matrix.MatrixOps.Tuple2;
import gbench.common.tree.LittleTree.IRecord;
import gbench.appdemo.ggplot.aes.Aesthetics;
import gbench.appdemo.ggplot.layer.Layer;
import gbench.appdemo.ggplot.plot.panel.SinkPanel;

import static gbench.common.matlib.matrix.MatrixOps.Tuple2.*;
import static gbench.common.matlib.MatlibCanvas.Range.NP2;
import static gbench.common.matlib.MatlibCanvas.Range.P2;
import static java.lang.Math.*;


/**
 * 
 * 点
 * @author gbench
 *
 */
public class GeomPoint extends Layer {

    public GeomPoint(DFrame dataframe, Aesthetics mappings) {
        super(mappings, dataframe);
    }

    /**
    * aes 映射参数: <br>
    * x：点的位置坐标 水平,必须 <br>
    * y: 点的位置坐标 垂直 ,必须<br>
    * flip: 是否反转（调换x,y位置）， true 反转,false 不反转,默认为 false<br>
    * size: 点的半径尺寸,默认为0d <br>
    * border: 点边缘线的厚度 为0到1的浮点数,默认为1,表示实心的点 <br>
    * linedensity：中间连线的点数量,单位长度补充的中间节点的数量,默认为0即没有中间连线<br>
    * 
    * @param dataframe 数据框
    * @param mappings 绘图属性映射
    */
    public static GeomPoint geom_point(DFrame dataframe, Aesthetics mappings) {
        return new GeomPoint(dataframe, mappings);
    }

    /**
     * aes 映射参数: <br>
     * x：点的位置坐标 水平,必须 <br>
     * y: 点的位置坐标 垂直 ,必须<br>
     * flip: 是否反转（调换x,y位置）， true 反转,false 不反转,默认为 false<br>
     * size: 点的半径尺寸,默认为0d <br>
     * border: 点边缘线的厚度 为0到1的浮点数,默认为1,表示实心的点<br>
     * linedensity：中间连线的点数量,单位长度补充的中间节点的数量,默认为0即没有中间连线<br>
     * 
     * @param mappings 绘图属性映射
     */
    public static GeomPoint geom_point(Aesthetics mappings) {
        return new GeomPoint(null, mappings);
    }

    /**
     * 图层计算 <br><br>
     * aes 映射参数: <br>
     * x：点的位置坐标 水平,必须
     * y: 点的位置坐标 垂直 ,必须<br>
     * flip: 是否反转（调换x,y位置）， true 反转,false 不反转,默认为 false<br>
     * size: 点的半径尺寸,默认为0d <br>
     * border: 点边缘线的厚度 为0到1的浮点数,默认为1,表示实心的点 <br>
     * linedensity：中间连线的点数量,单位长度补充的中间节点的数量,默认为0即没有中间连线<br>
     * @return 像素点的流
     */
    @Override
    public Stream<Tuple2<Tuple2<Double, Double>, int[]>> evaluate(SinkPanel panel) {

        final var x = this.get("x");
        final var y = this.get("y");
        final var rgbstr = this.getAes("color", "#ff0000"); // 提取颜色映射
        final var size = this.getAes("size", 0d); // 提取映射尺寸,半径，point size
        final var start = size-this.getAes("border", 1d)*size; // 提取边线的厚度
        final var rgb = ImageOps.rgb(rgbstr);// 像素点颜色
        final var linedensity = this.getAes("linedensity", 0);// 连线密度:点之间连线的中间点数量
        final var points = (this.getAes("flip", false) // 是否做xy旋转
            ? Tuple2.zip(y.data(), x.data())
            : Tuple2.zip(x.data(), y.data())
            ).collect(Collectors.toList());
        final var num = 50;// 分点数量
        final var pipe = Pipe.DUMMY((Tuple2<Double, Double>)null)
        .bind(point-> NP2(PI, num)
            .flatMap(theta -> P2(start, size, num).map(r -> // r:半径,theta:角度弧度
                new Double[] {r*cos(theta),r*sin(theta)})
            ).map(NVec::new).map(e->e.affine_offset(point._1(),point._2()))
            .map(nvec->TUP2(TUP2(nvec.get(0), nvec.get(1)),rgb))
        ).functionalize();
        
        final var ss = IRecord.sliding(points,2, 1,true).stream().flatMap(ee->{
            final var s = ee[0];
            final var e = ee[1];
            final var dx = e._1()-s._1(); // x 变化量
            final var dy = e._2()-s._2(); // y 变化量
            
            if(e._1().equals(s._1())) {
                final var n = Math.abs(s._2()-e._2())*linedensity; // 点数量
                return Range.P2(s._2(),e._2(),n).map(elem->NVec.of(s._1(),elem));
            }else {
                final var k = dy/dx;// 斜率
                final var n = Math.sqrt(ImageOps.square(dx)+ImageOps.square(dy))*linedensity;// 计算距离长度所包含的点数量
                return Range.P2(s._1(),e._1(),n).dblStream().map(elem->NVec.of(elem,k*(elem-s._1())+s._2()));
            }//if
        });
        
        final var ret = size==0d // 当size为0的时候不进行放大
            ? points.stream().map(e -> TUP2(e, rgb))
            : points.stream().flatMap(pipe);
            
        return Stream.concat(ss.map(nvec->TUP2(TUP2(nvec.get(0), nvec.get(1)),rgb)),ret);
    }

}
